package com.atguigu.gmall.service.impl;

import com.atguigu.gmall.constant.MqConst;
import com.atguigu.gmall.constant.RedisConst;
import com.atguigu.gmall.entity.PrepareSeckillOrder;
import com.atguigu.gmall.entity.SeckillProduct;
import com.atguigu.gmall.entity.UserSeckillSkuInfo;
import com.atguigu.gmall.mapper.SeckillProductMapper;
import com.atguigu.gmall.result.RetVal;
import com.atguigu.gmall.result.RetValCodeEnum;
import com.atguigu.gmall.service.SeckillService;
import com.atguigu.gmall.util.DateUtil;
import com.atguigu.gmall.util.MD5;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class SeckillServiceImpl extends ServiceImpl<SeckillProductMapper, SeckillProduct> implements SeckillService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public List<SeckillProduct> findSeckillProduct() {
        //从redis查询,有结果返回
        List<SeckillProduct> values = redisTemplate.boundHashOps(RedisConst.SECKILL_PRODUCT).values();
        if (CollectionUtils.isEmpty(values)){
            //如果redis中商品数量为空，查询当日是否有秒杀商品
            QueryWrapper<SeckillProduct> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("status", 1)
                    .gt("stock_count", 0)
                    .eq("DATE_FORMAT(start_time,'%Y-%m-%d')", DateUtil.formatDate(new Date()));
            values = baseMapper.selectList(queryWrapper);
            if (!CollectionUtils.isEmpty(values)){
                //todo 有秒杀商品未上架，通知商品上架
                rabbitTemplate.convertAndSend(MqConst.SCAN_SECKILL_EXCHANGE,  MqConst.SCAN_SECKILL_ROUTE_KEY, "");
            }
        }
        return values;
    }

    @Override
    public SeckillProduct getSeckillProductBySkuId(Long skuId) {
        return (SeckillProduct) redisTemplate.boundHashOps(RedisConst.SECKILL_PRODUCT).get(skuId.toString());
    }

    @Override
    public boolean checkSeckillProductStatusBySkuId(Long skuId) {
        String state = (String) redisTemplate.opsForValue().get(RedisConst.SECKILL_STATE_PREFIX + skuId.toString());
        if (state==null)
            return false; //没有商品的状态位
        return RedisConst.CAN_SECKILL.equals(state);
    }

    @Override
    public void sendRabbitMqMessage(String userId, Long skuId) {
        UserSeckillSkuInfo userSeckillSkuInfo = new UserSeckillSkuInfo();
        //哪个用户购买了哪个商品
        userSeckillSkuInfo.setUserId(userId);
        userSeckillSkuInfo.setSkuId(skuId);
        rabbitTemplate.convertAndSend(MqConst.PREPARE_SECKILL_EXCHANGE, MqConst.PREPARE_SECKILL_ROUTE_KEY,userSeckillSkuInfo);
    }

    @Override
    public void prepareSeckill(UserSeckillSkuInfo userSeckillSkuInfo) {
        String userId = userSeckillSkuInfo.getUserId();
        Long skuId = userSeckillSkuInfo.getSkuId();
        //判断用户是否下过单，如果没有下过单，则存入redis中，记录用户抢单
        boolean flag = redisTemplate.opsForValue().setIfAbsent(RedisConst.PREPARE_SECKILL_USERID_SKUID + userId,
                skuId,
                RedisConst.PREPARE_SECKILL_LOCK_TIME, TimeUnit.SECONDS);
        if (!flag){
            return;//秒杀过，无法继续秒杀
        }
        //秒杀量是否足够？ + 库存量-1
        String stockSkuId = (String) redisTemplate.boundListOps(RedisConst.SECKILL_STOCK_PREFIX + skuId).rightPop();
        if (StringUtils.isEmpty(stockSkuId))
            return;//秒杀量不够了，无法继续秒杀；
        //保存预订单信息
        PrepareSeckillOrder prepareSeckillOrder = new PrepareSeckillOrder();
        prepareSeckillOrder.setUserId(userId);
        prepareSeckillOrder.setBuyNum(1);
        SeckillProduct seckillProduct = this.getSeckillProductBySkuId(skuId); //获取商品详情信息
        prepareSeckillOrder.setSeckillProduct(seckillProduct); //保存到预订单中
        prepareSeckillOrder.setPrepareOrderCode(MD5.encrypt(userId+skuId));   //设置订单码
        redisTemplate.boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).put(userId, prepareSeckillOrder);
        //更新秒杀商品库存信息
        this.updateSeckillProductForRedis(skuId);
    }

    @Override
    public RetVal hasQualified(Long skuId, String userId) {
        //a.如果预下单中有用户的信息 prepare:seckill:userId:skuId3
        boolean flag = redisTemplate.hasKey(RedisConst.PREPARE_SECKILL_USERID_SKUID + userId);
        if (flag){
            //查询redis中是否具有该用户的预订单信息
            /**
             * b.如果预下单中有用户的订单信息 说明用户预抢单成功 有抢购资格
             * prepareSeckillOrder如果这个为空 代表该用户已经下单完成 因为下单成功之后会删除预购订单 此时走下面第二步
             * prepareSeckillOrder如果不为空 代表该用户具有秒杀资格
             */
            PrepareSeckillOrder prepareSeckillOrder = (PrepareSeckillOrder) redisTemplate.boundHashOps(RedisConst.SECKILL_PRODUCT).get(userId);
            if(prepareSeckillOrder!=null){
                return RetVal.build(prepareSeckillOrder, RetValCodeEnum.PREPARE_SECKILL_SUCCESS);
            }
        }
        //第二步: 如果用户已经秒杀了同样的该商品 (同一个用户购买同一个商品才能到这里)
        Integer orderId =(Integer) redisTemplate.boundHashOps(RedisConst.BOUGHT_SECKILL_USER_ORDER).get(userId);
        if(orderId!=null){
            return RetVal.build(orderId,RetValCodeEnum.SECKILL_ORDER_SUCCESS);
        }

        return RetVal.build(null,RetValCodeEnum.SECKILL_RUN);
    }

    @Override
    public PrepareSeckillOrder getseckillPrepareOrderInfoByUserId(String userId) {
        return (PrepareSeckillOrder) redisTemplate.boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).get(userId);
    }

    @Override
    public void deletePrepareSeckillOrder(String userId, Long orderId) {
        //根据userId删除预订单
        redisTemplate.boundHashOps(RedisConst.PREPARE_SECKILL_USERID_ORDER).delete(userId);
        //为了让用户只能购买一次 将秒杀的商品数据保存到缓存
        redisTemplate.boundHashOps(RedisConst.BOUGHT_SECKILL_USER_ORDER).put(userId, orderId.toString());

    }

    private void updateSeckillProductForRedis(Long skuId) {
        //更新redis
        SeckillProduct seckillProduct = this.getSeckillProductBySkuId(skuId);
        seckillProduct.setNum(seckillProduct.getNum() -1);
        redisTemplate.opsForValue().set(RedisConst.SECKILL_PRODUCT + skuId.toString(), seckillProduct);
        //更新数据库 todo 在秒杀结束之后更新
        QueryWrapper<SeckillProduct> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("sku_id", seckillProduct);
        baseMapper.update(seckillProduct, queryWrapper);

    }

    private void updateSeckillProductForMysql(Long skuId) {
        SeckillProduct seckillProduct = this.getSeckillProductBySkuId(skuId);
        QueryWrapper<SeckillProduct> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("sku_id", seckillProduct);
        baseMapper.update(seckillProduct, queryWrapper);
    }
}
